
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <unistd.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <string.h>
#include <malloc.h>
#include <sstream>
#include "define.h"
#include "Debug.h"
#include "NantxConn.h"
#include "NantSock.h"
#include "AntDefine.h"

using namespace netproxy;
using namespace std;

bool NantxConn::Connect( )
{
        m_lCurtime = time(NULL);
	if(m_socket == 0)
	{
		struct hostent *he;
		struct sockaddr_in their_addr;
		he = gethostbyname(m_sHostname.c_str());
		if((m_socket = socket(AF_INET,SOCK_STREAM,0))==-1)
		{
			perror("socketi error!");
                        m_bLive = false;
                        m_socket = 0;
			return false;
		}
                if(he == NULL)
                {
                       cerr<<"gethostbyname return NULL "<<endl;
                       return false;
                }

		their_addr.sin_family = AF_INET;
		their_addr.sin_port = htons( m_iPort );
		their_addr.sin_addr = *((struct in_addr *)he->h_addr);
		bzero(&(their_addr.sin_zero), 8);
		if(connect(m_socket, (struct sockaddr *)&their_addr, sizeof(struct sockaddr)) ==-1){
                        ERRO << "connect error: "<< m_sHostname<<":" << strerror(errno) << endl;  
			close(m_socket);
			m_socket = 0;
			m_bLive = false;
			return false;
		}
/**
设置超时是因为，可能由于对方的原因导致此socket处于阻塞状态，从而使得整个netproxy处于阻塞状态！

 */
                struct timeval timeout = {1, 0};
                setsockopt(m_socket, SOL_SOCKET, SO_SNDTIMEO, &timeout,  sizeof(struct timeval));
                setsockopt(m_socket, SOL_SOCKET, SO_RCVTIMEO, &timeout,  sizeof(struct timeval));

		int flags = 1;
		setsockopt(m_socket, SOL_SOCKET, SO_REUSEADDR, (void *)&flags, sizeof(flags));
		setsockopt( m_socket, SOL_SOCKET, SO_KEEPALIVE, (void *)&flags, sizeof(flags));
		struct linger ling = {0, 0};
		setsockopt( m_socket, SOL_SOCKET, SO_LINGER, (void *)&ling, sizeof(ling));
		setsockopt( m_socket, IPPROTO_TCP, TCP_NODELAY, (void *)&flags, sizeof(flags));

                m_iCounts = 0;
	}  
	m_bLive = true;
	return m_bLive;
}

void NantxConn:: disConnect()
{
	if(m_socket != 0)
	{
		INFO <<" close "<< m_sHostname.c_str()<<":"<<m_iPort <<": key ="<< m_iKey <<endl;
                close(m_socket);
		m_socket  = 0;
		m_lLasttime = time(NULL);
		m_bLive = false;
	}
	else
		m_bLive = false;
}

bool NantxConn:: ReConnect()
{
	if(m_socket != 0)
	{
                close(m_socket);
		m_socket  = 0;
	}
        return Connect();
}

int NantxConn:: Proxy(const char *data, unsigned dlen, char **wbuf, unsigned &wsize, unsigned &wbytes, bool bReply)
{
	if(m_socket <= 0 ) 
		return -1;
        assert(*wbuf != NULL);
        m_iCounts++;	
        int slen = Nantx_Write(m_socket,(char *) data, dlen);
//       int slen = send(m_socket, data, dlen, 0);
        DEBUG5("send length=", slen, m_sHostname,":", m_iPort);
        if(slen ==-1) {
                perror("send");
                disConnect();
                return -1;
        }
        if(!bReply){
                DEBUG1("needn't reply, so return");
                return 0;
        }
        int readlen = read_network( m_socket, wbuf, wsize, wbytes); 
        if(readlen > 0)
        {
                DEBUG2("Yes, read length = ", readlen);
                wbytes = readlen;
                return 0;
        }
        DEBUG2("read from server, but readlen = ", readlen );
        disConnect();
        return -2;
}


int NantxConn::read_network(int sock, char **wbuf, unsigned &wsize, unsigned &wbytes)
{
        unsigned ulen = sizeof(unsigned);
        int iRes = -1;
        wbytes = 0;
        iRes = recv(sock, *wbuf, 2*ulen  , 0);
        if(iRes != 2*(int)ulen )
        {
              DEBUG2("read header error : ", errno);
       //       if(iRes == -1 && errno == EAGAIN)
       //              return 0;
              wbytes = 0;
              return -1;
        }
        wbytes = iRes;
        unsigned waitLen = 2 * ulen ;
        bcopy(*wbuf, &waitLen, ulen );
        DEBUG2("should read length = ", waitLen);

/*
正常情况下（数据遵从协议),超时不会起到作用，因为系统以长度为第一判断标准！所以timeout可以设的稍长些！
*/
        int64_t tmMaxTimer = Nantx_Microtime() + 200000 ; // in microseconds
        while ( waitLen > wbytes )
        {
		if (waitLen > wsize ) {
			char *new_rbuf = (char *)realloc(*wbuf, waitLen + 1 );
			if (!new_rbuf) {
				wbytes = 0; /* ignore what we read */
				ERRO<< "SERVER_ERROR out of memory reading request" << endl;
				return -1;
			}
			*wbuf = new_rbuf;
                        wsize = waitLen + 1;
		}
                const int64_t tmMicroLeft = tmMaxTimer - Nantx_Microtime();
                if ( tmMicroLeft<=0 )
                {
                        ERRO <<"time out"<< endl;
                        wbytes = 0;
                        return -1;  
                      //   break; // timed out
		}
		// try to receive next chunk
                int avail = waitLen - wbytes;
                iRes = recv(sock, *wbuf + wbytes, avail, 0);
                if(iRes > 0)
                {
                        wbytes += iRes;
                        continue;
                }

                // if there was eof, we're done
                if ( iRes== 0 )
                {
                        wbytes = 0;
                        // cout<< strerror( ECONNRESET ) <<": or socket lost!" << endl;;
                        return -1;  //   wbytes;
                }
               // if there was EINTR, retry
                if ( iRes==-1 )
                {
                        // ERRO<<" recv error: " << strerror( errno ) << endl;
                        if (errno == EAGAIN || errno == EINTR || EWOULDBLOCK)
                              continue ; //break;
                        wbytes = 0; 
                        return -1;
                }
	}
	if(waitLen == wbytes)
		return  wbytes;
	wbytes = 0;
	return -1;
}

